<?php

namespace Common\wx;

use Common\wx\wxMsgCrypt\WXBizMsgCrypt;
use Think\Log;

class WechatThirdPf
{

	/**第三方平台推送消息类型-*/
	const INFOTYPE_COMP_TICKET = "component_verify_ticket";
	
	/**第三方平台推送消息类型-取消授权*/
	const INFOTYPE_UNAUTH = "unauthorized";

	/**API用于获取第三方平台令牌（component_access_token）**/
	const componentToken = "https://api.weixin.qq.com/cgi-bin/component/api_component_token";
	
	/**获取预授权码。预授权码用于公众号授权时的第三方平台方安全验证  POST*/
	const createPreauthcode = "https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode?component_access_token=";
	
	/**使用授权码换取公众号的授权信息*/
	const  queryAuth = "https://api.weixin.qq.com/cgi-bin/component/api_query_auth?component_access_token=";
	
	/**获取（刷新）授权公众号的令牌 POST*/
	const authorizerToken  = "https://api.weixin.qq.com/cgi-bin/component/api_authorizer_token?component_access_token=";

	/**获取授权方的账户信息 POST*/
	const  authorizerInfo = "https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_info?component_access_token=";

	/**获取授权方的选项设置信息 POST*/
	const  authorizerOption = "https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_option?component_access_token=";
	
	/**设置授权方的选项信息 POST*/
	const  stAuthorizerOption = "https://api.weixin.qq.com/cgi-bin/component/api_set_authorizer_option?component_access_token=";
	
	/**网页授权第一步*/
	const WebOauthFirst = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=AID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE&component_appid=CPT#wechat_redirect";
	
	/**网页授权第二步*/
	const WebOauthSecond = "https://api.weixin.qq.com/sns/oauth2/component/access_token?appid=AID&code=CODE&grant_type=authorization_code&component_appid=CPT&component_access_token=CACT";
	
	/**网页授权第三步,刷新access_token*/
	const WebOauthThird = "https://api.weixin.qq.com/sns/oauth2/component/refresh_token?appid=AID&grant_type=refresh_token&component_appid=CPT&component_access_token=CACT&refresh_token=RTEN";
	
	/**网页授权第四步,通过网页授权access_token获取用户基本信息（需授权作用域为snsapi_userinfo）*/
	const WebOauthFourth = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";

	private $thridopenAppid;
	private $thridopenAppsecret;
	private $thridopenDomain;
	private $thridopenToken;
	private $thridopenKey;

	public function __construct($type, $domain) {
		$this->thridopenAppid = C("wx_".$type."_thirdopen_appid");
		$this->thridopenAppsecret = C("wx_".$type."_thirdopen_appsecret");
		$this->thridopenDomain = empty($domain)?C("wx_".$type."_thirdopen_domain"):$domain;
		$this->thridopenToken = C("wx_".$type."_thirdopen_token");
		$this->thridopenKey = C("wx_".$type."_thirdopen_key");
	}
    
	/**
	 * 从db获取componentToken
	 */
	public function getcomponentTokenFromDB(){
		$thridopenAppid = $this->thridopenAppid;
		
		//查询数据库是否存在componentToken，存在的话是否过期
		$tokenStr = "code='componentToken_".$thridopenAppid."' ";
		$sysconfig = M('sysconfig');
		$sysConfig1 = $sysconfig->where($tokenStr)->find();
		if($sysConfig1){
			if(isset($sysConfig1['value'])){
				//如果距离上次更新时间大于90分钟，5400s,则从微信重新获取 componentToken过期时间为7200s
				// addRequest('getcomponentTokenFromDB', $sysConfig1['value'],time() - strtotime($sysConfig1['createTime']));
				if(time() - strtotime($sysConfig1['createTime']) >= 5400 || $sysConfig1['value']==''){
					$componentToken = $this->getComponentTokenFromWx();
					$data['id'] = $sysConfig1['id'];
					$data['value'] = $componentToken;
					$data['createTime'] = date('Y-m-d H:i:s');
					$sysconfig->save($data);
					return $componentToken;
				}else{
					return $sysConfig1['value'];
				}
			}else{
				$componentToken = $this->getComponentTokenFromWx();
				$data['id'] = $sysConfig1['id'];
				$data['value'] = $componentToken;
				$data['createTime'] = date('Y-m-d H:i:s');
				$sysconfig->save($data);
				return $componentToken;
			}
		}else{//数据库无相关记录
			$componentToken = $this->getComponentTokenFromWx();
			// addRequest('getcomponentTokenFromDB', $componentToken, json_encode($sysConfig1));
			$sysConfig2 = array();
			$sysConfig2['code'] = "componentToken_".$thridopenAppid;
			$sysConfig2['value'] = $componentToken;
			$sysConfig2['remark'] = "微信第三方平台component_access_token"; 
			$sysConfig2['createTime'] = date('Y-m-d H:i:s');
			$sysconfig->add($sysConfig2);
			return $componentToken;
		}
	}

	/**
	 * 获取第三方平台access_token
	 */
	public function getComponentTokenFromWx(){
		$url = self::componentToken;
		$thridopenAppid = $this->thridopenAppid;
		$ticketStr = "code='componentVerifyTicket_".$thridopenAppid."' ";
		$sysconfig = M('sysconfig');
		$sysConfig1 = $sysconfig->where($ticketStr)->find();
		if($sysConfig1){
			$componentVerifyTicket = $sysConfig1['value'];
			$thridopenAppsecret = $this->thridopenAppsecret;

			$data = array(
			'component_appid'=>$thridopenAppid,
			'component_appsecret'=>$thridopenAppsecret,
			'component_verify_ticket'=>$componentVerifyTicket
		    );
		    $result = $this->http_post($url, self::json_encode($data));
		    if ($result)
		    {
		        $json = json_decode($result,true);
				if (!$json || !empty($json['errcode'])) {
				   
					Log::write("获取第三方平台access_token:errcode=".$json['errcode'].",errmsg=".$json['errmsg'], "ERR");
					return false;
				}
				return $json['component_access_token'];
		    }

		}else{
			return false;
		}
	}

	/***
	 * 获取预授权码
	 */
	public function getPreauthcodeFromWx(){
		$componentAccessToken = $this->getcomponentTokenFromDB();
		$thridopenAppid = $this->thridopenAppid;
		$data = array(
			'component_appid'=>$thridopenAppid
		    );
		
		$result = $this->http_post(self::createPreauthcode.$componentAccessToken,self::json_encode($data));
		
		if ($result)
		{
			$json = json_decode($result,true);
			if (!$json || !empty($json['errcode'])) {
				Log::write("获取预授权码:errcode=".$json['errcode'].",errmsg=".$json['errmsg'], "ERR");
				return false;
			}
			return $json['pre_auth_code'];
		}
		return false;
	}

	/**
	 * 使用授权码换取公众号的授权信息
	 */
	public function getQueryAuthFromWx($auth_code){
		$componentAccessToken = $this->getcomponentTokenFromDB();
		$thridopenAppid = $this->thridopenAppid;
		$auth_code = $_GET['auth_code'];
		$data = array(
			'component_appid'=>$thridopenAppid,
			'authorization_code'=>$auth_code
		    );
		$result = $this->http_post(self::queryAuth.$componentAccessToken,self::json_encode($data));
		if ($result)
		{
			$json = json_decode($result,true);
			if (!$json || !empty($json['errcode'])) {
				Log::write("使用授权码换取公众号的授权信息:errcode=".$json['errcode'].",errmsg=".$json['errmsg'], "ERR");
				return false;
			}
			return $json;
		}
		return false;
	}

	 /**
     * 获取授权方的账户信息-微信接口
     */
	public function getAuthorizerinfoFromWx($authorizer_appid){
		$componentAccessToken = $this->getcomponentTokenFromDB();
		$thridopenAppid = $this->thridopenAppid;
		$data = array(
			'component_appid'=>$thridopenAppid,
			'authorizer_appid'=>$authorizer_appid
		    );
		$result = $this->http_post(self::authorizerInfo.$componentAccessToken,self::json_encode($data));
		if ($result)
		{
			$json = json_decode($result,true);
			if (!$json || !empty($json['errcode'])) {
				Log::write("获取授权方的账户信息:errcode=".$json['errcode'].",errmsg=".$json['errmsg'], "ERR");
				return false;
			}
			return $json;
		}
		return false;
	}

	/**
	 * 获取（刷新）授权公众号的令牌-微信接口
	 */
	public function getAuthorizertokenFromWx($authorizer_appid, $authorizer_refresh_token){
		$componentAccessToken = $this->getcomponentTokenFromDB();
		$thridopenAppid = $this->thridopenAppid;
		$data = array(
			'component_appid'=>$thridopenAppid,
			'authorizer_appid'=>$authorizer_appid,
			'authorizer_refresh_token'=>$authorizer_refresh_token
		    );
		$result = $this->http_post(self::authorizerToken.$componentAccessToken,self::json_encode($data));
		// addRequest('getAuthorizertokenFromWx', json_encode($data), json_encode($result));
		if ($result)
		{
			$json = json_decode($result,true);
			if (!$json || !empty($json['errcode'])) {
				Log::write("获取（刷新）授权公众号的令牌:errcode=".$json['errcode'].",errmsg=".$json['errmsg'], "ERR");
				return false;
			}
			return $json;
		}
		return false;
	}

	/**
	 * 网页授权第一步返回封装地址
	 */
	public function webOauthFirstFromWx($appId, $redirectUri, $scope, $state){
		$component_appid = $this->thridopenAppid;
		$search = array("AID", "CPT", "REDIRECT_URI", "SCOPE", "STATE");  
		$replace = array($appId, $component_appid, urlencode($this->thridopenDomain.$redirectUri), $scope, $state); 
		return str_replace($search, $replace, self::WebOauthFirst);
	}

	/**
	 * 代发起网页授权第二步--微信接口
	 */
	public function webOauthSecondFromWx($appid, $code){
		$component_appid = $this->thridopenAppid;
		$component_access_token = $this->getcomponentTokenFromDB();
		$search = array("AID", "CODE","CPT", "CACT");  
		$replace = array($appid, $code, $component_appid, $component_access_token); 
		$url = str_replace($search, $replace, self::WebOauthSecond);
		$result = $this->http_get($url);
		if ($result)
		{
			$json = json_decode($result,true);
			if (isset($json['errcode'])) {
				Log::write("微信第三方平台代公众号发起网页授权第二步:errcode=".$json['errcode'].",errmsg=".$json['errmsg'].",code=".$code, "WARN");
				return false;
			}
			return $json;
		}
		return false;

	}

	/**
	 * 代发起网页授权第三步--微信接口
	 */
	public function webOauthThirdFromWx($appid, $refresh_token){
		$component_appid = $this->thridopenAppid;
		$component_access_token = $this->getcomponentTokenFromDB();
		$search = array("AID","CPT", "CACT", "RTEN");  
		$replace = array($appid, $component_appid, $component_access_token, $refresh_token); 
		$url = str_replace($search, $replace, self::WebOauthThird);
		$result = $this->http_get($url);
		if ($result)
		{
			$json = json_decode($result,true);
			if (isset($json['errcode'])) {
				Log::write("微信第三方平台代公众号发起网页授权第三步:errcode=".$json['errcode'].",errmsg=".$json['errmsg'].",code=".$code, "WARN");
				return false;
			}
			return $json;
		}
		return false;
	}

	/**
	 * 代发起网页授权第四步--微信接口
	 */
	public function webOauthFourthFromWx($access_token, $openid){
		$search = array("OPENID","ACCESS_TOKEN");  
		$replace = array($openid, $access_token); 
		$url = str_replace($search, $replace, self::WebOauthFourth);
		$result = $this->http_get($url);
		if ($result)
		{
			$json = json_decode($result,true);
			if (isset($json['errcode'])) {
				Log::write("微信第三方平台代公众号发起网页授权第四步:errcode=".$json['errcode'].",errmsg=".$json['errmsg'].",code=".$code, "WARN");
				return false;
			}
			return $json;
		}
		return false;
	}

	/**
	 * 推送component_verify_ticket协议
	 * @param xml
	 * @return
	 * @throws Exception
	 */
	public function procePostInfo(){
		Log::write("-----------------------proceMessage--------------------", "INFO");
		// 第三方发送消息给公众平台
		$encodingAesKey = $this->thridopenKey;
		$token = $this->thridopenToken;
		$timeStamp = $_GET['timestamp'];
		$nonce = $_GET['nonce'];
		$appId = $this->thridopenAppid;
		$msg_sign = $_GET['msg_signature'];
		$pc =new WXBizMsgCrypt($token, $encodingAesKey, $appId);
    
		$encryptMsg = file_get_contents('php://input');
		$xml_tree = new \DOMDocument();
		$xml_tree->loadXML($encryptMsg);
		$array_e = $xml_tree->getElementsByTagName('Encrypt');
		$encrypt = $array_e->item(0)->nodeValue;

		$format = "<xml><ToUserName><![CDATA[toUser]]></ToUserName><Encrypt><![CDATA[%s]]></Encrypt></xml>";
		$from_xml = sprintf($format, $encrypt);

		// 第三方收到公众号平台发送的消息
		$msg = '';
		$errCode = $pc->decryptMsg($msg_sign, $timeStamp, $nonce, $from_xml, $msg);
		// addRequest('shouqianmyname', json_encode($errCode), json_encode($from_xml));
		if ($errCode == 0) {
			 Log::write("解密后: " . $msg , "INFO");
		} else {
			Log::write("errCode = ".$errCode, "INFO");
		}
		
		$xml = new \DOMDocument();
		$xml->loadXML($msg);

		$array_f = $xml->getElementsByTagName('InfoType');
		$infoType = $array_f->item(0)->nodeValue;

		//Log::write("infoType = ".$infoType, "INFO");

		$array_e = $xml->getElementsByTagName('ComponentVerifyTicket');
		$component_verify_ticket = $array_e->item(0)->nodeValue;

		//Log::write("ComponentVerifyTicket = ".$component_verify_ticket, "INFO");

		if($infoType == 'component_verify_ticket'){ //处理推送消息为component_verify_ticket

			$sysconfig = M('sysconfig');
			$tokenStr = " code = 'componentVerifyTicket_".$appId."' ";
			$sysConfig1 = $sysconfig->where($tokenStr)->find();
			if(!$sysConfig1){
				$sysConfig1['code'] = "componentVerifyTicket_".$appId;
				$sysConfig1['value'] = $component_verify_ticket;
				$sysConfig1['remark'] = "第三方平台ComponentVerifyTicket";
				$sysConfig1['createTime'] = date('Y-m-d H:i:s');
				$sysconfig->add($sysConfig1);
				return "success";
			}else{
				$sysConfig1['value'] = $component_verify_ticket;
				$sysConfig1['createTime'] = date('Y-m-d H:i:s');
				$sysconfig->save($sysConfig1);
				return "success";
			}

		}else if($infoType == 'unauthorized'){//取消授权
			$array_appid = $xml->getElementsByTagName('AuthorizerAppid');
			$authorizerAppid = $array_appid->item(0)->nodeValue;
			$wx_user_tpfauth = M('wx_tpfauth');
			$userSaveWhere['authorizerAppid']=$authorizerAppid;
			$userTpFauth = $wx_user_tpfauth->where("authorizerAppid='$authorizerAppid'")->find();
			if($userTpFauth){
				$wx_user_tpfauth->where('id='.$userTpFauth['id'])->setField('state',1);
			}
			return "success";
		}
	}

	/**
	 * 
	 * @param xml
	 * @return
	 * @throws Exception
	 */
	public function proceMessage(){
		return "success";
	}

	/**
	 * GET 请求
	 * @param string $url
	 */
	private function http_get($url){
		$oCurl = curl_init();
		if(stripos($url,"https://")!==FALSE){
			curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);
			curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, FALSE);
			curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
		}
		curl_setopt($oCurl, CURLOPT_URL, $url);
		curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 );
		$sContent = curl_exec($oCurl);
		$aStatus = curl_getinfo($oCurl);
		curl_close($oCurl);
		if(intval($aStatus["http_code"])==200){
			return $sContent;
		}else{
			return false;
		}
	}

	/**
	 * POST 请求
	 * @param string $url
	 * @param array $param
	 * @param boolean $post_file 是否文件上传
	 * @return string content
	 */
	private function http_post($url,$param,$post_file=false){
		$oCurl = curl_init();
		if(stripos($url,"https://")!==FALSE){
			curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);
			curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, false);
			curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
		}
		if (is_string($param) || $post_file) {
			$strPOST = $param;
		} else {
			$aPOST = array();
			foreach($param as $key=>$val){
				$aPOST[] = $key."=".urlencode($val);
			}
			$strPOST =  join("&", $aPOST);
		}
		curl_setopt($oCurl, CURLOPT_URL, $url);
		curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 );
		curl_setopt($oCurl, CURLOPT_POST,true);
		curl_setopt($oCurl, CURLOPT_POSTFIELDS,$strPOST);
		$sContent = curl_exec($oCurl);
		$aStatus = curl_getinfo($oCurl);
		curl_close($oCurl);
		if(intval($aStatus["http_code"])==200){
			return $sContent;
		}else{
			return false;
		}
	}

	/**
	 * 微信api不支持中文转义的json结构
	 * @param array $arr
	 */
	static function json_encode($arr) {
		$parts = array ();
		$is_list = false;
		//Find out if the given array is a numerical array
		$keys = array_keys ( $arr );
		$max_length = count ( $arr ) - 1;
		if (($keys [0] === 0) && ($keys [$max_length] === $max_length )) { //See if the first key is 0 and last key is length - 1
			$is_list = true;
			for($i = 0; $i < count ( $keys ); $i ++) { //See if each key correspondes to its position
				if ($i != $keys [$i]) { //A key fails at position check.
					$is_list = false; //It is an associative array.
					break;
				}
			}
		}
		foreach ( $arr as $key => $value ) {
			if (is_array ( $value )) { //Custom handling for arrays
				if ($is_list)
					$parts [] = self::json_encode ( $value ); /* :RECURSION: */
				else
					$parts [] = '"' . $key . '":' . self::json_encode ( $value ); /* :RECURSION: */
			} else {
				$str = '';
				if (! $is_list)
					$str = '"' . $key . '":';
				//Custom handling for multiple data types
				if (!is_string ( $value ) && is_numeric ( $value ) && $value<2000000000)
					$str .= $value; //Numbers
				elseif ($value === false)
				$str .= 'false'; //The booleans
				elseif ($value === true)
				$str .= 'true';
				else
					$str .= '"' . addslashes ( $value ) . '"'; //All other things
				// :TODO: Is there any more datatype we should be in the lookout for? (Object?)
				$parts [] = $str;
			}
		}
		$json = implode ( ',', $parts );
		if ($is_list)
			return '[' . $json . ']'; //Return numerical JSON
		return '{' . $json . '}'; //Return associative JSON
	}
}

?>